//build: 
//  GTK3: g++ pollfd.cpp `pkg-config --libs --cflags gtk+-3.0 x11` -o /tmp/x11src  # -g -Wall
//  Gtk4: g++ pollfd.cpp `pkg-config --libs --cflags gtk4 x11` -o /tmp/x11src  # -g -Wall

/* needed to break into 'Display' struct internals. */
#define XLIB_ILLEGAL_ACCESS

#include <X11/Xlib.h> // Every Xlib program must include this
#include <assert.h>   // I include this to test return values the lazy way
#include <glib.h>
#include <gtk/gtk.h>

#if GTK_MAJOR_VERSION  > 3
    #include <gdk/x11/gdkx.h>
#else
    #include <gdk/gdkx.h>
#endif

typedef struct _x11_source {
    GSource source;
    Display *dpy;
    Window w;
    GPollFD gpfd;
} x11_source_t;

static gboolean x11_fd_prepare(GSource *source, gint *timeout)
{
    //printf("calling prepare\n");
    *timeout = -1;
    return FALSE;
}

static gboolean x11_fd_check (GSource *source)
{
    //printf("calling check\n");
    auto xsrc = reinterpret_cast<x11_source_t *>(source);
    return xsrc->gpfd.events & xsrc->gpfd.revents;
}


static void on_source_callback(XConfigureEvent *xce)
{
    g_print("Receive ConfigureNotify, %dx%d@%dx%d\n", xce->width, xce->height, xce->x, xce->y);
}

static gboolean x11_fd_dispatch(GSource* source, GSourceFunc callback, gpointer user_data)
{
    //printf("calling dispatch\n");
    auto xsrc = reinterpret_cast<x11_source_t *>(source);
    if(!(xsrc->gpfd.revents & G_IO_IN))
    {
        return G_SOURCE_REMOVE;
    }
    XEvent e, *xce = NULL;
    while (XCheckWindowEvent(xsrc->dpy,
                             xsrc->w,
                             StructureNotifyMask, /* | EnterWindowMask, */
                             &e))
    {
        if (e.type == ConfigureNotify)
        {
            xce = &e;
        }
    }
    if(xce)
    {
        callback(xce);
    }
    return G_SOURCE_CONTINUE;
}
static void x11_fd_finalize(GSource* source)
{
    printf("calling finalize\n");
    Display *dpy = ((x11_source_t*)source)->dpy;
    XCloseDisplay(dpy);
}

static int handle_x_error(Display *display, XErrorEvent *event)
{
    char error_msg[1024];
    XGetErrorText(display, event->error_code, error_msg, sizeof(error_msg));
    fprintf(stderr, "X Error: %s (code %d)\n", error_msg, event->error_code);
    return 0; // 返回 0 表示不中止程序
}

int main(int argc, char *argv[])
{
    XSetErrorHandler(handle_x_error);
      

#if GTK_MAJOR_VERSION  > 3
    gtk_init();
    auto gwin = gtk_window_new();
    gtk_window_present(GTK_WINDOW(gwin));

    GdkSurface* surface = gtk_native_get_surface(GTK_NATIVE (gwin));
    Window   xw = gdk_x11_surface_get_xid(surface);
    //Display *dpy = gdk_x11_display_get_xdisplay(gtk_widget_get_display(gwin));
#else
    gtk_init(&argc, &argv);
    auto gwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_show(gwin);
    Window xw = GDK_WINDOW_XID(gtk_widget_get_window(gwin));
#endif
    Display *dpy = XOpenDisplay(NULL);
    XSelectInput(dpy, xw, StructureNotifyMask);
    XSync(dpy, FALSE);

    GSourceFuncs x11_source_funcs = {
        x11_fd_prepare,
        x11_fd_check,
        x11_fd_dispatch,
        x11_fd_finalize, /* finalize */
        NULL, /* closure_callback */
        NULL /* closure_marshal */
    };

    GSource *gsrc = g_source_new(&x11_source_funcs, sizeof(x11_source_t));
    g_source_set_callback(gsrc, GSourceFunc(on_source_callback), NULL, NULL);
    auto x11src = reinterpret_cast<x11_source_t*>(gsrc);
    x11src->dpy = dpy;
    x11src->w = xw;
    x11src->gpfd = {dpy->fd, G_IO_IN | G_IO_HUP | G_IO_ERR, 0};
    g_source_add_poll(gsrc, &x11src->gpfd);
    g_source_attach(gsrc, NULL);


    printf("Decorated:%d\n", gtk_window_get_decorated(GTK_WINDOW(gwin)));
    auto loop = g_main_loop_new(NULL, FALSE);
    g_signal_connect_swapped(gwin, "destroy", G_CALLBACK(g_main_loop_quit), loop);
    g_main_loop_run(loop);

    return 0;
}
